/*******************************************************************************
 * Copyright (c) 2007 University of Oslo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     University of Oslo - initial API and implementation
 *******************************************************************************/

package com.cea.papyrus.diagram.sequence.sedi.edit;

import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.getCovereds;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.eclipse.draw2d.IFigure;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.Request;
import org.eclipse.gef.RequestConstants;
import org.eclipse.uml2.uml.InteractionUse;
import org.eclipse.uml2.uml.Lifeline;
import org.eclipse.uml2.uml.UMLPackage;

import com.cea.papyrus.core.editor.ModelManager;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagram.sequence.sedi.edit.manager.InteractionUseEditManager;
import com.cea.papyrus.diagram.sequence.sedi.edit.policy.DeletionEditPolicy;
import com.cea.papyrus.diagram.sequence.sedi.edit.policy.GraphConnectionPolicy;
import com.cea.papyrus.diagram.sequence.sedi.edit.policy.InteractionUseDirectEditPolicy;
import com.cea.papyrus.diagram.sequence.sedi.figures.AnchorContributor;
import com.cea.papyrus.diagram.sequence.sedi.figures.AnchoringFigure;
import com.cea.papyrus.diagram.sequence.sedi.figures.ILabeledFigure;
import com.cea.papyrus.diagram.sequence.sedi.figures.InteractionUseFigure;
import com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil;
import com.cea.papyrus.diagraminterchange2.di2.Diagram;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Property;

/**
 * Edit part for Interaction Uses.
 * 
 * @author Andreas Limyr
 * @author frankd@ifi.uio.no
 */
public class IOEditPart extends AnchoringEditPart<InteractionUse> {
	
	/**
	 * The {@link Diagram} that contains the {@link IOEditPart}
	 */
	private Diagram diagram; 
	
    public IOEditPart(GraphElement view, ModelManager modelManager) {
        super(view, modelManager);
        
        // Get diagram on EditPart creation instead of calling getDiagram
        // dynamically
        // Dynamic call is problematic during deletion (from Model or Diagram)
        // because the graphical container may be null.
        diagram = LookForElement.getDiagram(getGraphNode());
    }

    @Override
    protected IFigure createFigure() {
        InteractionUseFigure figure = new InteractionUseFigure();
        figure.setMessageAccess(this);
        return figure;
    }

    @Override
    protected void createEditPolicies() {
        super.createEditPolicies();
        installEditPolicy(EditPolicy.DIRECT_EDIT_ROLE, new InteractionUseDirectEditPolicy());
        //installEditPolicy(EditPolicy.GRAPHICAL_NODE_ROLE, new SeDiNodeEditPolicy());
		
        // Manage connection between this element and others
		this.installEditPolicy(
				EditPolicy.GRAPHICAL_NODE_ROLE, 
				new GraphConnectionPolicy() );
        // Add edit policy to handle deletion requests (replace SeDiSubpartEditPolicy)
		installEditPolicy(
				EditPolicy.COMPONENT_ROLE,
		        new DeletionEditPolicy());
    }

    @Override
    protected void refreshVisualsLabel() {
        // InteractionUses displays the name of the referred to interaction
        // rather than the name of the InteractionUse model element itself.
        final InteractionUse iuse = getUmlElement();
        final ILabeledFigure iuseFigure = (ILabeledFigure) getFigure();
        iuseFigure
                .setLabel((iuse.getRefersTo() != null && iuse.getRefersTo().getName() != null) ? iuse
                        .getRefersTo().getName()
                        : "<no name>");
    }

    private Set<Lifeline> contributingTo = new HashSet<Lifeline>();

    @Override
    protected void refreshVisuals() {
        super.refreshVisuals();

        // Update anchor contribution to covered lifelines
        final Set<Lifeline> obsolete = new HashSet<Lifeline>(contributingTo);
        for (Lifeline lifeline : getCovereds(getUmlElement())) {
            obsolete.remove(lifeline);

            if (!contributingTo.contains(lifeline)) {
                AnchoringFigure lifelineFigure = getLifelineFigure(lifeline);
                lifelineFigure.addAnchorContributor((AnchorContributor) getFigure());
                contributingTo.add(lifeline);
            }
        }
        for (Lifeline lifeline : obsolete) {
            AnchoringFigure lifelineFigure = getLifelineFigure(lifeline);
            lifelineFigure.removeAnchorContributor((AnchorContributor) getFigure());
            contributingTo.remove(lifeline);
        }
    }

    @SuppressWarnings("unchecked")
    private AnchoringFigure getLifelineFigure(Lifeline lifeline) {
        Map<Object, AnchoringEditPart> editPartRegistry =
                (Map<Object, AnchoringEditPart>) getViewer().getEditPartRegistry();
        GraphElement lifelineView = ModelUtil.getGraphNode(lifeline, diagram);
        AnchoringEditPart lifelineEP = editPartRegistry.get(lifelineView);
        return (AnchoringFigure) lifelineEP.getFigure();
    }

    private void removeAllAnchorContributions() {
        for (Lifeline lifeline : contributingTo) {
            AnchoringFigure lifelineFigure = getLifelineFigure(lifeline);
            if (lifelineFigure != null)
                lifelineFigure.removeAnchorContributor((AnchorContributor) getFigure());
            // else: give up, the lifeline is deleted from the diagram
        }
        contributingTo.clear();
    }

    @Override
    public void performRequest(Request request) {
        if (request.getType() == RequestConstants.REQ_OPEN)
            performOpenRequest();
        else
            super.performRequest(request);
    }

    protected void performOpenRequest() {
        // TODO Open editor with the diagram for the referred-to interaction
    }

    @Override
    protected void performDirectEdit(final Request request) {
        InteractionUse iuse = getUmlElement();
        if (iuse.getRefersTo() != null)
            new InteractionUseEditManager(this, getCellEditorLocator()).show();
    }

    @Override
    protected void umlModelChanged(Notification evt) {
        switch (evt.getFeatureID(UMLPackage.class)) {
        case UMLPackage.INTERACTION_USE__REFERS_TO:
//            if (evt.getOldValue() != null)
//                deactivateInteraction((Interaction) evt.getOldValue());
//            if (evt.getNewValue() != null)
//                activateInteraction((Interaction) evt.getNewValue());
//            // fallthrough
        case UMLPackage.INTERACTION_USE__COVERED:
        case UMLPackage.INTERACTION__NAME:
            refreshVisuals();
            break;
        }
        super.umlModelChanged(evt);
    }
    
    // --- Activation ---
    @Override
    public void activate() {
		if (this.isActive()) {
			return;
		}
		super.activate();
		// Start listening for changes in the model
		((Notifier)this.getModel()).eAdapters().add(graphElementListener);
		
		Iterator<Property> propertiesIterator= ((GraphNode) this.getModel()).getProperty().iterator();
		while(propertiesIterator.hasNext()){
			((Notifier)propertiesIterator.next()).eAdapters().add(graphElementListener);
		}
		
		// Start listening for changes on referenced interaction
		if (getUmlElement().getRefersTo() != null) {
			getUmlElement().getRefersTo().eAdapters().add(graphElementListener);
		}
    }

    @Override
    public void deactivate() {
		if (!this.isActive()) {
			return;
		}

		// Stop listening for changes in the model
		((Notifier)this.getModel()).eAdapters().remove(graphElementListener);
		
		Iterator<Property> propertiesIterator= ((GraphNode) this.getModel()).getProperty().iterator();
		while(propertiesIterator.hasNext()){
			((Notifier) propertiesIterator.next()).eAdapters().remove(graphElementListener);
		}		
				
		// Stop listening for changes on referenced interaction
		if (getUmlElement().getRefersTo() != null) {
			getUmlElement().getRefersTo().eAdapters().remove(graphElementListener);
		}
    

		super.deactivate();
    }
}
